﻿using System;
using System.IO;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
using System.Collections.Generic;

using ImageCombinerCore.ExMethod;

namespace ImageCombinerCore
{
    public class ImageCombiner
    {
        string mOutputImagePath;
        bool mSavedDeleteSourceImages;
        List<CombineInfoStruct> mCombineInfoList;

        public string OutputImagePath { get { return mOutputImagePath; } set { mOutputImagePath = value; } }
        public bool SavedDeleteSourceImages { get { return mSavedDeleteSourceImages; } set { mSavedDeleteSourceImages = value; } }


        public ImageCombiner()
        {
            mCombineInfoList = new List<CombineInfoStruct>();
        }

        public void Reset(CombineInfoStruct[] newCombineInfoArr)
        {
            mCombineInfoList.Clear();
            mCombineInfoList.AddRange(newCombineInfoArr);
        }

        public CombineInfoStruct[] GetCombineInfoArr()
        {
            var result = new CombineInfoStruct[mCombineInfoList.Count];
            mCombineInfoList.CopyTo(result);

            return result;
        }

        public void AddCombineInfo(CombineInfoStruct combineInfo)
        {
            mCombineInfoList.Add(combineInfo);
        }

        public void RemoveCombineInfo(CombineInfoStruct combineInfo)
        {
            mCombineInfoList.Remove(combineInfo);
        }

        public void ClearCombineInfo()
        {
            mCombineInfoList.Clear();
        }

        public void MoveCombineInfoToBefore(CombineInfoStruct combineInfo)
        {
            mCombineInfoList.SwapToBefore(combineInfo);
        }

        public void MoveCombineInfoToAfter(CombineInfoStruct combineInfo)
        {
            mCombineInfoList.SwapToAfter(combineInfo);
        }

        public void MoveCombineInfoToFirst(CombineInfoStruct combineInfo)
        {
            mCombineInfoList.MoveToFirst(combineInfo);
        }

        public void MoveCombineInfoToLast(CombineInfoStruct combineInfo)
        {
            mCombineInfoList.MoveToLast(combineInfo);
        }

        public bool SavePreset(string savePath)
        {
            OutputInfoStruct outputInfo = new OutputInfoStruct();
            outputInfo.CombineInfoArr = mCombineInfoList.ToArray();
            outputInfo.CombinedDeleteSourceImages = mSavedDeleteSourceImages;
            outputInfo.OutputImagePath = mOutputImagePath;

            try
            {
                XmlSerializationHelper.SerializationToDisk(savePath, outputInfo);
            }
            catch
            {
                return false;
            }

            return true;
        }

        public bool LoadPreset(string loadPath)
        {
            try
            {
                var deserializationInfo = XmlSerializationHelper.DeSerializationFromDisk<OutputInfoStruct>(loadPath);
                Reset(deserializationInfo.CombineInfoArr);
                mOutputImagePath = deserializationInfo.OutputImagePath;
                mSavedDeleteSourceImages = deserializationInfo.CombinedDeleteSourceImages;
            }
            catch
            {
                return false;
            }

            return true;
        }

        public Image ExecuteCombine()
        {
            if (mCombineInfoList.Count == 0) return null;
            if (mCombineInfoList.Count == 1) return new Bitmap(mCombineInfoList[0].ImagePath);

            var imagePaths = mCombineInfoList.ConvertAll(m => WildcardPathToGeneralPath(m.ImagePath)).ToList();
            var bitmaps = imagePaths.ConvertAll(m => new Bitmap(m));

            var maxWidth = bitmaps.Max(m => m.Width);
            var maxHeight = bitmaps.Max(m => m.Height);

            var resultBitmap = new Bitmap(maxWidth, maxHeight);

            Blend(resultBitmap, bitmaps[0], BlendModeEnum.Normal);

            for (int i = 1; i < mCombineInfoList.Count; i++)
            {
                var item = mCombineInfoList[i];
                var currentBitmap = bitmaps[i];

                Blend(resultBitmap, currentBitmap, item.BlendMode, item.Opacity);
            }

            for (int i = 0; i < bitmaps.Count; i++)
            {
                bitmaps[i].Dispose();
                bitmaps[i] = null;
            }

            return resultBitmap;
        }

        public bool ExecuteCombineAndSaved()
        {
            var resultImage = ExecuteCombine();

            if (resultImage != null)
            {
                resultImage.Save(mOutputImagePath, ImageFormat.Jpeg);
                resultImage.Dispose();

                if (mSavedDeleteSourceImages)
                {
                    mCombineInfoList.ForEach(m =>
                    {
                        var targetPath = WildcardPathToGeneralPath(m.ImagePath);
                        File.Delete(targetPath);
                    });
                }

                return true;
            }
            else
            {
                return false;
            }
        }

        string WildcardPathToGeneralPath(string wildcardPath)
        {
            var dir = Path.GetDirectoryName(wildcardPath);
            var fileName = Path.GetFileName(wildcardPath);
            var matchFiles = Directory.GetFiles(dir, fileName);

            if (matchFiles.Length > 0) return matchFiles[0];
            return "";
        }

        void Blend(Bitmap xBitmap, Bitmap yBitmap, BlendModeEnum mode, float opacity = 1)
        {
            var xLockedBitmap = new LockedBitmap(xBitmap);
            var yLockedBitmap = new LockedBitmap(yBitmap);

            xLockedBitmap.LockBits();
            yLockedBitmap.LockBits();

            for (int y = 0; y < yLockedBitmap.Height; y++)
            {
                for (int x = 0; x < xLockedBitmap.Width; x++)
                {
                    if (x >= yLockedBitmap.Width) break;


                    var xBitmapPixel = xLockedBitmap.GetPixel(x, y);
                    var yBitmapPixel = yLockedBitmap.GetPixel(x, y);

                    var resultPixel = Color.White;

                    if (opacity < 1)
                        yBitmapPixel = ColorBlendHelper.Transparency(yBitmapPixel, xBitmapPixel, opacity);

                    switch (mode)
                    {
                        case BlendModeEnum.Normal:
                            resultPixel = ColorBlendHelper.Normal(xBitmapPixel, yBitmapPixel);
                            break;
                        case BlendModeEnum.Lighten:
                            resultPixel = ColorBlendHelper.Light(xBitmapPixel, yBitmapPixel);
                            break;
                        case BlendModeEnum.Darken:
                            resultPixel = ColorBlendHelper.Darken(xBitmapPixel, yBitmapPixel);
                            break;
                        case BlendModeEnum.Overlay:
                            resultPixel = ColorBlendHelper.Overlay(xBitmapPixel, yBitmapPixel);
                            break;
                    }

                    xLockedBitmap.SetPixel(x, y, resultPixel);
                }
            }

            xLockedBitmap.UnlockBits();
            yLockedBitmap.UnlockBits();
        }
    }
}
